import * as qiniu from 'qiniu-js'
import { isArray } from '@/utils/common/typeof'
import { generatePath } from '@/utils/common/file'
import CustomAxiosInstance from './instance'
import { getServiceEnvConfig } from '~/.env-config'

const limitReq = {}

export function createRequest(axiosConfig, backendConfig) {
    const customInstance = new CustomAxiosInstance(axiosConfig, backendConfig)

    async function asyncRequest(param) {
        const { url, axiosConfig } = param
        if (axiosConfig?.key) {
            if (limitReq[axiosConfig.key]) {
                return Promise.reject('请求正在执行，不要重复操作')
            }
            limitReq[axiosConfig.key] = true
        }

        const method = param.method || 'get'
        const { instance } = customInstance
        const params = {
            instance,
            method,
            url,
            config: param.axiosConfig ?? {},
            data: undefined
        }
        if (method.toLowerCase() == 'get' && param.data) {
            params.config.params = param.data
        } else if (param.data) {
            params.data = param.data
        }
        const res = await getRequestResponse(params)

        if (axiosConfig?.key) {
            delete limitReq[axiosConfig.key]
        }

        return res
    }

    function get(url, data, config) {
        return asyncRequest({
            url,
            method: 'get',
            data,
            axiosConfig: config
        })
    }

    async function tryGet(url, data, config) {
        const { error, result } = await asyncRequest({
            url,
            method: 'get',
            data,
            axiosConfig: config
        })
        if (!error) {
            return Promise.resolve(result)
        }

        return Promise.reject(result)
    }

    function post(url, data, config) {
        return asyncRequest({
            url,
            method: 'post',
            data,
            axiosConfig: config
        })
    }

    async function uploadToQiniu(
        files,
        qiniuPath,
        qiniuSn,
        next,
        complete,
        error,
        putExtra,
        config
    ) {
        const { domain, token } = await getQiniuDomainToken(qiniuSn)


        if (isArray(files)) {
            return Promise.all(
                files.map((file) =>
                    uploadToQiniu(
                        file,
                        qiniuPath,
                        qiniuSn,
                        next,
                        complete,
                        error,
                        putExtra,
                        config
                    )
                )
            )
        }
        return new Promise((resolve, reject) => {
            let path = generatePath(files.name, qiniuPath)
            if (path.startsWith('/')) path = path.substring(1)
            qiniu.upload(files, path, token, putExtra, config).subscribe({
                next: (res) => {
                    if (next) {
                        next({
                            size: res.total.size,
                            loaded: res.total.loaded,
                            percent: res.total.percent
                        })
                    }
                },
                error: (err) => {
                    if (error) {
                        error({ file: files, path, error: err })
                    }
                    reject(err)
                },
                complete: (res) => {
                    if (complete) {
                        complete({
                            url: domain + '/' + res.key,
                            key: res.key,
                            hash: res.hash
                        })
                    }
                    resolve(domain + '/' + res.key)
                }
            })
        })
    }

    async function uploadToServer(files, savePath, next, complete, error) {
        const { serverUploadApi } = getServiceEnvConfig(import.meta.env)
        if (!serverUploadApi) {
            throw new Error('请在.env-config.ts中配置serverUploadApi后再使用该方法')
        }
        if (isArray(files)) {
            return Promise.all(
                files.map((file) => uploadToServer(file, savePath, next, complete, error))
            )
        }
        return new Promise((resolve, reject) => {
            let path = generatePath(files.name, savePath)
            if (path.startsWith('/')) path = path.substring(1)
            tryPost(
                `${serverUploadApi}?path=${path}`,
                { file: files },
                {
                    headers: {
                        'Content-Type': 'multipart/form-data'
                    },
                    onUploadProgress: function (progressEvent) {
                        if (next) {
                            const progress = progressEvent.total
                                ? (progressEvent.loaded / progressEvent.total) * 100
                                : 0
                            next({
                                size: progressEvent.total ?? 0,
                                loaded: progressEvent.loaded,
                                percent: progress
                            })
                        }
                    }
                }
            )
                .then((res) => {
                    if (complete) {
                        complete({ url: res.data.url, id: res.data.id })
                    }
                    resolve(res.data.url)
                })
                .catch((err) => {
                    if (error) {
                        error({ file: files, path, error: err })
                    }
                    reject(err)
                })
        })
    }

    async function tryPost(url, data, config) {
        const { error, result } = await asyncRequest({
            url,
            method: 'post',
            data,
            axiosConfig: config
        })
        if (!error) {
            return Promise.resolve(result)
        }

        return Promise.reject(result)
    }

    async function getQiniuDomainToken(sn) {
        const res = await tryGet(`/commonConfig/getQiniu?bucketSn=${sn ?? ''}`)
        return res.data
    }

    function put(url, data, config) {
        return asyncRequest({
            url,
            method: 'put',
            data,
            axiosConfig: config
        })
    }
    function patch(url, data, config) {
        return asyncRequest({
            url,
            method: 'put',
            data,
            axiosConfig: config
        })
    }

    function handleDelete(url, config) {
        return asyncRequest({ url, method: 'delete', axiosConfig: config })
    }

    return {
        get,
        tryGet,
        post,
        tryPost,
        uploadToQiniu,
        uploadToServer,
        getQiniuDomainToken,
        put,
        patch,
        delete: handleDelete
    }
}

async function getRequestResponse(params) {
    const { instance, method, url, data, config } = params

    let res
    if (method === 'get' || method === 'delete') {
        res = await instance[method](url, config)
    } else {
        res = await instance[method](url, data, config)
    }
    return res
}
